﻿
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using WgFrame.Net.Peer;

namespace WgFrame.Net.Socket
{
    public abstract class IWgSocket
    {
        protected internal PeerBase peerBase;
        protected readonly ConnectionProtocol Protocol; 
        public string ConnectAddress;
		protected IWgPeerListener Listener
		{
			get
			{
				return peerBase.Listener;
			}
		}

		public WgSocketState State { get; protected set; }

		public bool Connected
		{
			get
			{
				return State == WgSocketState.Connected;
			}
		}

		public string ServerAddress { get; protected set; }
		public static string ServerIpAddress { get; protected set; }
		public int ServerPort { get; protected set; }
		public bool AddressResolvedAsIpv6 { get; protected set; }

		public string UrlProtocol { get; protected set; }

		public string UrlPath { get; protected set; }

		public IWgSocket(PeerBase peerBase)
		{
			if (peerBase == null)
			{
				throw new Exception("Can't init without peer");
			}
			Protocol = peerBase.usedTransportProtocol;
			this.peerBase = peerBase;
			ConnectAddress = this.peerBase.ServerAddress;
		}

		public virtual bool Connect()
		{
			bool result;
			if (State > WgSocketState.Disconnected)
			{
				if (peerBase.debugOut >= DebugLevel.ERROR)
				{
					peerBase.Listener.DebugReturn(DebugLevel.ERROR, "Connect() failed: connection in State: " + State.ToString());
				}
				result = false;
			}
			else
			{
				if (peerBase == null || Protocol != peerBase.usedTransportProtocol)
				{
					result = false;
				}
				else
				{
					string host;
					ushort hostPort;
					string urlProtocol;
					string urlPath;
					if (!TryParseAddress(peerBase.ServerAddress, out host, out hostPort, out urlProtocol, out urlPath))
					{
						if (peerBase.debugOut >= DebugLevel.ERROR)
						{
							peerBase.Listener.DebugReturn(DebugLevel.ERROR, "Failed parsing address: " + peerBase.ServerAddress);
						}
						result = false;
					}
					else
					{
						ServerIpAddress = string.Empty;
						ServerAddress = host;
						ServerPort = (int)hostPort;
						UrlProtocol = urlProtocol;
						UrlPath = urlPath;
						if (peerBase.debugOut >= DebugLevel.ALL)
						{
							Listener.DebugReturn(DebugLevel.ALL, string.Concat(new string[]
							{
								"IWgSocket.Connect() ",
								ServerAddress,
								":",
								ServerPort.ToString(),
								" this.Protocol: ",
								Protocol.ToString()
							}));
						}
						result = true;
					}
				}
			}
			return result;
		}

		public abstract bool Disconnect();

		public abstract WgSocketError Send(byte[] data, int length);

		public void HandleReceivedDatagram(byte[] inBuffer, int length)
		{
			peerBase.ReceiveIncomingCommands(inBuffer, length);
		}

		public bool ReportDebugOfLevel(DebugLevel levelOfMessage)
		{
			return peerBase.debugOut >= levelOfMessage;
		}

		public void EnqueueDebugReturn(DebugLevel debugLevel, string message)
		{
			peerBase.EnqueueDebugReturn(debugLevel, message);
		}

		protected internal void HandleException(StatusCode statusCode)
		{
			State = WgSocketState.Disconnecting;
			peerBase.EnqueueStatusCallback(statusCode);
			peerBase.EnqueueActionForDispatch(delegate
			{
				peerBase.Disconnect();
			});
		}

		protected internal bool TryParseAddress(string url, out string address, out ushort port, out string urlProtocol, out string urlPath)
		{
			address = string.Empty;
			port = 0;
			urlProtocol = string.Empty;
			urlPath = string.Empty;
			string addressAndPort = url;
			bool result;
			if (string.IsNullOrEmpty(addressAndPort))
			{
				result = false;
			}
			else
			{
				int idx = addressAndPort.IndexOf("://");
				if (idx >= 0)
				{
					urlProtocol = addressAndPort.Substring(0, idx);
					addressAndPort = addressAndPort.Substring(idx + 3);
				}
				idx = addressAndPort.IndexOf("/");
				if (idx >= 0)
				{
					urlPath = addressAndPort.Substring(idx);
					addressAndPort = addressAndPort.Substring(0, idx);
				}
				idx = addressAndPort.LastIndexOf(':');
				if (idx < 0)
				{
					result = false;
				}
				else
				{
					if (addressAndPort.IndexOf(':') != idx && (!addressAndPort.Contains("[") || !addressAndPort.Contains("]")))
					{
						result = false;
					}
					else
					{
						address = addressAndPort.Substring(0, idx);
						string portString = addressAndPort.Substring(idx + 1);
						bool gotPort = ushort.TryParse(portString, out port);
						result = gotPort;
					}
				}
			}
			return result;
		}

		protected internal IPAddress[] GetIpAddresses(string hostname)
		{
			IPAddress ipa = null;
			IPAddress[] result;
			if (IPAddress.TryParse(hostname, out ipa))
			{
				result = new IPAddress[]
				{
					ipa
				};
			}
			else
			{
				IPAddress[] addresses;
				try
				{
					IPHostEntry hostEntry = Dns.GetHostEntry(ServerAddress);
					addresses = hostEntry.AddressList;
				}
				catch (Exception ex)
				{
					if (ReportDebugOfLevel(DebugLevel.ERROR))
					{
						DebugLevel debugLevel = DebugLevel.ERROR;
						string str = "DNS.GetHostEntry() failed for: ";
						string serverAddress = ServerAddress;
						string str2 = ". Exception: ";
						Exception ex2 = ex;
						EnqueueDebugReturn(debugLevel, str + serverAddress + str2 + ((ex2 != null) ? ex2.ToString() : null));
					}
					HandleException(StatusCode.ExceptionOnConnect);
					return null;
				}
				Array.Sort<IPAddress>(addresses, new Comparison<IPAddress>(AddressSortComparer));
				if (ReportDebugOfLevel(DebugLevel.INFO))
				{
					string[] ips = (from x in addresses
									select string.Concat(new string[]
									{
						x.ToString(),
						" (",
						x.AddressFamily.ToString(),
						"(",
						((int)x.AddressFamily).ToString(),
						"))"
									})).ToArray<string>();
					string ipList = string.Join(", ", ips);
					if (ReportDebugOfLevel(DebugLevel.INFO))
					{
						EnqueueDebugReturn(DebugLevel.INFO, string.Concat(new string[]
						{
							ServerAddress,
							" resolved to ",
							ips.Length.ToString(),
							" address(es): ",
							ipList
						}));
					}
				}
				result = addresses;
			}
			return result;
		}

		private int AddressSortComparer(IPAddress x, IPAddress y)
		{
			int result;
			if (x.AddressFamily == y.AddressFamily)
			{
				result = 0;
			}
			else
			{
				result = ((x.AddressFamily == AddressFamily.InterNetworkV6) ? -1 : 1);
			}
			return result;
		}
	}
}
